iT邦幫忙

0

Day 21|資料驗證(Validation)與錯誤處理

c#
  • 分享至 

  • xImage
  •  

今日學習目標

  • 理解資料驗證(Validation)的用途與重要性
  • 認識 Data Annotations 資料註記
  • 學會使用 [Required] 與 [StringLength]
  • 在 CreateTaskDto 與 UpdateTaskDto 加入驗證規則
  • 了解 [ApiController] 的自動驗證功能
  • 學會使用 ValidationProblem() 回傳錯誤訊息
  • 區分 HTTP 400、404、500 狀態碼
  • 使用 Swagger UI 測試資料驗證功能

什麼是資料驗證(Validation)

想像使用者在 TaskFlow 新增任務時,輸入了以下資料:

{
  "title": ""
}

或者輸入一個長達 500 字的任務名稱。
如果沒有設定限制,這些資料就可能被儲存到資料庫中,造成資料品質不佳,甚至影響後續功能。
因此,我們要讓 TaskFlow 檢查使用者輸入的資料。
今天設定三個規則:
https://ithelp.ithome.com.tw/upload/images/20261023/201844307zp3Y4o6zs.png

了解 Data Annotations

Data Annotations 是 C# 提供的一組 Attribute(屬性標記)。

修改 CreateTaskDto.cs

使用昨日的專案

using System.ComponentModel.DataAnnotations;

namespace TaskFlow3.Api.DTOs
{
    public class CreateTaskDto
    {
        [Required(ErrorMessage = "任務名稱不可空白")]
        [StringLength(
            100,
            MinimumLength = 2,
            ErrorMessage = "任務名稱必須介於 2 到 100 個字元"
        )]
        public string Title { get; set; } = "";
    }
}

修改 UpdateTaskDto.cs

using System.ComponentModel.DataAnnotations;

namespace TaskFlow3.Api.DTOs
{
    public class UpdateTaskDto
    {
        [Required(ErrorMessage = "任務名稱不可空白")]
        [StringLength(
            100,
            MinimumLength = 2,
            ErrorMessage = "任務名稱必須介於 2 到 100 個字元"
        )]
        public string Title { get; set; } = "";

        public bool IsCompleted { get; set; }
    }
}

修改 TasksController.cs

using Microsoft.AspNetCore.Mvc;
using TaskFlow3.Api.DTOs;
using TaskFlow3.Api.Models;
using TaskFlow3.Api.Services;

namespace TaskFlow3.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class TasksController : ControllerBase
    {
        private readonly ITaskService _taskService;

        // 建構函式注入
        public TasksController(ITaskService taskService)
        {
            _taskService = taskService;
        }

        // GET /api/tasks
        // 查詢所有任務,支援篩選、搜尋、排序
        [HttpGet]
        public async Task<ActionResult<List<TaskDto>>> GetTasks(
            [FromQuery] bool? completed = null,
            [FromQuery] string? keyword = null,
            [FromQuery] string sort = "asc")
        {
            var tasks = await _taskService.GetAllAsync(
                completed,
                keyword,
                sort
            );

            var result = tasks.Select(t => ToDto(t)).ToList();

            return Ok(result);
        }

        // GET /api/tasks/1
        // 查詢指定任務
        [HttpGet("{id:int}")]
        public async Task<ActionResult<TaskDto>> GetTask(int id)
        {
            TaskItem? task = await _taskService.GetByIdAsync(id);

            if (task == null)
            {
                return NotFound(new
                {
                    message = "找不到這筆任務",
                    taskId = id
                });
            }

            return Ok(ToDto(task));
        }

        // POST /api/tasks
        // 新增任務
        [HttpPost]
        public async Task<ActionResult<TaskDto>> CreateTask(
            [FromBody] CreateTaskDto input)
        {
            // 檢查任務名稱是否為空白
            if (string.IsNullOrWhiteSpace(input.Title))
            {
                ModelState.AddModelError(
                    nameof(input.Title),
                    "任務名稱不可空白"
                );

                return ValidationProblem(ModelState);
            }

            // 移除前後空白
            string title = input.Title.Trim();

            // 檢查移除空白後的長度
            if (title.Length < 2 || title.Length > 100)
            {
                ModelState.AddModelError(
                    nameof(input.Title),
                    "任務名稱必須介於 2 到 100 個字元"
                );

                return ValidationProblem(ModelState);
            }

            // 呼叫 Service 新增任務
            TaskItem task = await _taskService.CreateAsync(title);

            return CreatedAtAction(
                nameof(GetTask),
                new { id = task.Id },
                ToDto(task)
            );
        }

        // PUT /api/tasks/1
        // 修改任務
        [HttpPut("{id:int}")]
        public async Task<IActionResult> UpdateTask(
            int id,
            [FromBody] UpdateTaskDto input)
        {
            // 檢查任務名稱是否為空白
            if (string.IsNullOrWhiteSpace(input.Title))
            {
                ModelState.AddModelError(
                    nameof(input.Title),
                    "任務名稱不可空白"
                );

                return ValidationProblem(ModelState);
            }

            string title = input.Title.Trim();

            // 檢查名稱長度
            if (title.Length < 2 || title.Length > 100)
            {
                ModelState.AddModelError(
                    nameof(input.Title),
                    "任務名稱必須介於 2 到 100 個字元"
                );

                return ValidationProblem(ModelState);
            }

            // 呼叫 Service 修改資料
            bool success = await _taskService.UpdateAsync(
                id,
                title,
                input.IsCompleted
            );

            if (!success)
            {
                return NotFound(new
                {
                    message = "找不到這筆任務",
                    taskId = id
                });
            }

            return NoContent();
        }

        // DELETE /api/tasks/1
        // 刪除任務
        [HttpDelete("{id:int}")]
        public async Task<IActionResult> DeleteTask(int id)
        {
            bool success = await _taskService.DeleteAsync(id);

            if (!success)
            {
                return NotFound(new
                {
                    message = "找不到這筆任務",
                    taskId = id
                });
            }

            // 刪除成功,回傳 204
            return NoContent();
        }

        // 將 Model 轉換成 DTO
        private static TaskDto ToDto(TaskItem task)
        {
            return new TaskDto
            {
                Id = task.Id,
                Title = task.Title,
                IsCompleted = task.IsCompleted
            };
        }
    }
}

認識 HTTP 錯誤狀態碼

資料驗證不只是阻止錯誤資料進入系統,也需要讓前端知道發生什麼問題。
https://ithelp.ithome.com.tw/upload/images/20261023/20184430xnNJXOQAUc.png
400:使用者傳送的資料有問題。
404:使用者要求的任務不存在。
500:伺服器內部發生未預期錯誤。


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言